#include "result.h"
#include "benchmark.h"

using namespace std;

//
// CConfigModule
//

const CONFIGNODE *CConfigModule::GetNodeByName(const CChString *find) const
{
    for (uint i=0; i<m_Nodes.size(); i++)
    {
        if (m_Nodes[i].name == *find)
            return &m_Nodes[i];
    }

    return NULL;
}


bool CConfigModule::ReadConfig(CTokenizer *tokens)
{
    try
    {
        const CChString *token = &tokens->GetTokenPtr()->token;
        while (tokens->Next(false))
        {
            if (*token == "modulename")
            {
                m_Name = *tokens->NextEqString();
            }
            else if (*token == "longname")
            {
                m_LongName = *tokens->NextEqString();
            }
            else if (*token == "moduleweight")
            {
                m_Weight = tokens->NextEqFloat();
            }
            else if (*token == "</module>")
            {
                return true;
            }
            else 
            {
                uint i;

                for (i=0; i<m_Nodes.size(); i++)
                {
                    if (m_Nodes[i].name == *token)
                    {
                        printf("Hiba: duplazott config: \"%s\"\n", token->c_str());
                        break;
                    }
                }

                if (i >= m_Nodes.size())
                {
                    CONFIGNODE node;

                    node.name = *token;
                    node.weight = tokens->NextEqFloat();

                    m_Nodes.push_back(node);
                }
            }
        }
    }
    catch (CTokenizerException &e)
    {
        printf("Hiba: \"%s\"", e.what());
        return false;
    }

    return true;
}                


//
// CConfigFile
//

CConfigFile::CConfigFile()
{
}


CConfigFile::~CConfigFile()
{
    Cleanup();
}


void CConfigFile::Cleanup()
{               
    for (uint i=0; i<m_Configs.size(); i++)
        SDELETE(m_Configs[i]);

    m_Configs.clear();
}


const CConfigModule *CConfigFile::GetModuleConfigByName(const CChString *find) const
{
    for (uint i=0; i<m_Configs.size(); i++)
    {
        if (*m_Configs[i]->GetName() == *find)
            return m_Configs[i];
    }

    return NULL;
}


void CConfigFile::Merge(CConfigFile *configs[], int count, const char *filename)
{
    FILE *f = fopen(filename, "wt");

    if (f == NULL)
    {
        printf("Hiba: nem tudtam megnyitni a fajlt: \"%s\"\n", filename);
        return;
    }

    const vector<CConfigModule*> &ref_configs = configs[0]->m_Configs;      // a 0. lesz alapjan lepunk vegig minden modulon

    for (uint i=0; i<ref_configs.size(); i++)                               // minden modulra
    {   
        fprintf(f, "<module>\n");
        fprintf(f, "modulename = \"%s\"\n", ref_configs[i]->GetName()->c_str());
        fprintf(f, "longname = \"%s\"\n", ref_configs[i]->GetLongName()->c_str());
        fprintf(f, "moduleweight = %.6f\n\n", ref_configs[i]->GetWeight());

        const vector<CONFIGNODE> &ref_nodes = ref_configs[i]->GetNodes();

        for (uint j=0; j<ref_nodes.size(); j++)                             // minden node-ra
        {
            double sum = 0;

            for (int k=0; k<count; k++)                                     // minden osszefesulendo configra
            {
                if (i >= configs[k]->m_Configs.size() || 
                    *ref_configs[i]->GetName() != *configs[k]->m_Configs[i]->GetName() ||    // van ilyen modul?
                    j >= configs[k]->m_Configs[i]->GetNodes().size() ||
                    ref_configs[i]->GetNodes()[j].name != configs[k]->m_Configs[i]->GetNodes()[j].name)
                {
                    puts("\n---------------------------------------------------------------------");
                    printf("HIBA: nem egyeznek a config fajlok\n");
                    puts("---------------------------------------------------------------------\n");
                    fclose(f);
                    return;
                }

                sum += configs[k]->m_Configs[i]->GetNodes()[j].weight;
            }

            fprintf(f, "%s = %.6f\n", ref_nodes[j].name.c_str(), sum / count);
        }

        fprintf(f, "</module>\n\n");
    }
    
    fclose(f);
}


bool CConfigFile::Load(const char *TXTfilename)
{
    Cleanup();

    CTokenizer tokens;

    if (!tokens.OpenFile(TXTfilename))
    {
        printf("Hiba a \"%s\" file megnyitasakor\n", TXTfilename);
        return false;
    }

    try
    {
        const CChString *token = &tokens.GetTokenPtr()->token;
        while (tokens.Next(false))
        {
            if (*token == "<module>")
            {
                CConfigModule *config = new CConfigModule;
                if (config->ReadConfig(&tokens))
                    m_Configs.push_back(config);
                else
                {
                    delete config;
                    return false;
                }
            }
        }
    }
    catch (CTokenizerException &e)
    {
        printf("Hiba a \"%s\" file beolvasasakor: \"%s\"\n", TXTfilename, e.what());
        return false;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////


RESULTSFILEINFO::RESULTSFILEINFO()
{
    comment = "none";
    compiler = "unknown";
    created = "unknown";
    display_name = "unknown";
    hw = "unknown";
    lib = "unknown";
    os = "unknown";
    gettime_precision = "0";
    pb_version = PBENCH_VERSION;
}


void RESULTSFILEINFO::Print(FILE *out) const
{
    fprintf(out, "pbench_version = %d\n", pb_version);
    fprintf(out, "gettimeprecision = \"%s\"\n", gettime_precision.c_str());
    fprintf(out, "displayname = \"%s\"\n", display_name.c_str());
    fprintf(out, "os = \"%s\"\n", os.c_str());
    fprintf(out, "hw = \"%s\"\n", hw.c_str());
    fprintf(out, "compiler = \"%s\"\n", compiler.c_str());
    fprintf(out, "lib = \"%s\"\n", lib.c_str());
    fprintf(out, "created = \"%s\"\n", created.c_str());
    fprintf(out, "comment = <text>%s</text>\n", comment.c_str());
}


bool RESULTSFILEINFO::OnReadResult(CTokenizer *tokens)
{
    const CChString *token = &tokens->GetTokenPtr()->token;

    if (*token == "pbench_version")
    {   
        pb_version = tokens->NextEqInt();
    }            
    else if (*token == "displayname")
    {
        display_name = *tokens->NextEqString();
    }
    else if (*token == "os")
    {
        os = *tokens->NextEqString();
    }
    else if (*token == "hw")
    {
        hw = *tokens->NextEqString();
    }
    else if (*token == "compiler")
    {
        compiler = *tokens->NextEqString();
    }
    else if (*token == "lib")
    {
        lib = *tokens->NextEqString();
    }
    else if (*token == "created")
    {
        created = *tokens->NextEqString();
    }
    else if (*token == "comment")
    {
        comment = *tokens->NextEqTextTag();
    }
    else if (*token == "gettimeprecision")
    {
        gettime_precision = *tokens->NextEqString();
    }
    else
        return false;

    return true;
}

//
// CResultModule
//

bool CResultModule::ReadResults(CTokenizer *tokens)
{
    try
    {
        const CChString *token = &tokens->GetTokenPtr()->token;
        while (tokens->Next(false))
        {
            if (*token == "modulename")
            {
                m_ModuleName = *tokens->NextEqString();
            }
            else if (*token == "</module>")
            {
                return true;
            }
            else 
            {
                uint i;

                for (i=0; i<m_Nodes.size(); i++)
                {
                    if (m_Nodes[i].name == *token)
                    {
                        printf("Hiba: duplazott result: \"%s\"\n", token->c_str());
                        break;
                    }
                }

                if (i >= m_Nodes.size())
                {
                    RESULTNODE node;

                    node.name = *token;
                    node.result = tokens->NextEqFloat();

                    m_Nodes.push_back(node);
                }
            }
        }
    }
    catch (CTokenizerException &e)
    {
        printf("Hiba: \"%s\"", e.what());
        return false;
    }


    return true;
}

//
// CResultFile
//


CResultFile::~CResultFile()
{
    Cleanup();
}


void CResultFile::Cleanup()
{
   for (uint i=0; i<m_Modules.size(); i++)
        SDELETE(m_Modules[i]);

    m_Modules.clear();
}


bool CResultFile::Load(const char *TXTfilename)
{
    Cleanup();

    CTokenizer tokens;

    if (!tokens.OpenFile(TXTfilename))
    {
        printf("Hiba a \"%s\" file megnyitasakor\n", TXTfilename);
        return false;
    }

    try
    {
        const CChString *token = &tokens.GetTokenPtr()->token;
        while (tokens.Next(false))
        {
            if (*token == "<module>")
            {
                CResultModule *result = new CResultModule;
                if (result->ReadResults(&tokens))
                    m_Modules.push_back(result);
                else
                    delete result;
            }
            else if (m_Info.OnReadResult(&tokens) == false)
            {
                // ismeretlen valtozo: hagyjuk ki az " = valami" reszt :)
                tokens.Next(true);      
                tokens.Next(true);
            }
        }
    }
    catch (CTokenizerException &e)
    {
        printf("Hiba a \"%s\" file beolvasasakor: \"%s\"\n", TXTfilename, e.what());
        return false;
    }

    if (m_Info.pb_version != PBENCH_VERSION)
    {
        printf("Hibas PB verzio (%d helyett %d) ebben a fajlban: \"%s\"\n", PBENCH_VERSION, m_Info.pb_version, TXTfilename);
        return false;
    }

    return true;

}


const RESULTNODE *CResultFile::GetResult(const CChString *modulename, const CChString *nodename) const
{
    for (uint i=0; i<m_Modules.size(); i++)
    {
        if (*m_Modules[i]->GetName() == *modulename)
        {
            const vector<RESULTNODE> &nodes = *m_Modules[i]->GetNodes();
            for (unsigned j=0; j<nodes.size(); j++)
            {
                if (nodes[j].name == *nodename)
                    return &nodes[j];
            }
            return NULL;
        }
    }
    return NULL;
}


//
// CResultManager
//

CResultManager::~CResultManager()
{
    Cleanup();
}


bool CResultManager::LoadFile(const char *filename)
{   
    if (m_Files.size() >= MAX_PLATFORMS)
        return false;

    CResultFile *f = new CResultFile;

    if (f->Load(filename))
    {
        m_Files.push_back(f);
        return true;
    }
    else
        delete f;

    return false;
}


void CResultManager::Cleanup()
{
    for (uint i=0; i<m_Files.size(); i++)
        SDELETE(m_Files[i]);

    m_Files.clear();
}


bool CResultManager::GenerateOutput(unsigned reference_file)        // reference_file: ehhez viszonyitjuk a tobbi gep erteket
{
    // kimenet: m_TotalOutput, m_ModuleOutput, m_NodeOutput
    // TODO: parameterbe tenni a kimenetet? Vagy ez a CResultManager, itt a helye? :)

    m_NodeOutput.clear();
    m_ModuleOutput.clear();

    if (m_Files.size() == 0 || reference_file >= m_Files.size())
        return false;

    const vector<CResultModule*> &refmodules = *m_Files[reference_file]->GetModules();
    m_ModuleOutput.reserve(refmodules.size());

    m_TotalOutput.filenames.reserve(m_Files.size());
    memset(m_TotalOutput.values, 0, sizeof(m_TotalOutput.values));
    memset(m_TotalOutput.times, 0, sizeof(m_TotalOutput.times));
    memset(m_TotalOutput.weighted_times, 0, sizeof(m_TotalOutput.weighted_times));
    m_TotalOutput.maxvalue = -1;

    for (uint i=0; i<m_Files.size(); i++)
        m_TotalOutput.filenames.push_back(*m_Files[i]->GetDisplayName());


    // ciklus minden modulra
    for (unsigned m=0; m<refmodules.size(); m++)
    {                     
        const CConfigModule *pModuleConfig = m_Config.GetModuleConfigByName(refmodules[m]->GetName());

        if (pModuleConfig == NULL)
        {
            assert(!"hianyzik egy modulnak a konfigja");
            //printf("Hiba: nincs config ehhez a modulhoz: \"%s\"\n", refmodules[m]->GetName()->c_str());
            continue;        
        }

        if (pModuleConfig->GetWeight() == 0)
            continue;
        
        const vector<RESULTNODE> &nodes = *refmodules[m]->GetNodes();        

        BENCHMARK_MODULE moduleout;
        moduleout.name = *pModuleConfig->GetLongName();        
        moduleout.maxvalue = -1;
        moduleout.firstnode = (int)m_NodeOutput.size();
        memset(moduleout.values, 0, sizeof(moduleout.values));
        memset(moduleout.times, 0, sizeof(moduleout.times));
        memset(moduleout.weighted_times, 0, sizeof(moduleout.weighted_times));
       
        // ciklus a modul minden eredmenyere
        for (uint i=0; i<nodes.size(); i++)
        {
            const CONFIGNODE *config = pModuleConfig->GetNodeByName(&nodes[i].name);

            if (config == NULL)
            {
                //printf("Hiba: nincs config ehhez az eredmenyhez: \"%s\"\n", nodes[i].name.c_str());
                assert(!"hianyzik egy reszeredmenynek a konfigja");
                continue;
            }

            if (config->weight == 0)
                continue;

            BENCHMARK_NODE out;
            out.name = config->name;

            float currentvalue = nodes[i].result;

            // ciklus a tobbiek eredmenyere
            for (unsigned f=0; f<m_Files.size(); f++)
            {
                const RESULTNODE *result = m_Files[f]->GetResult(pModuleConfig->GetName(), &config->name);

                if (result && currentvalue)
                {
                    out.values[f] = currentvalue / result->result;
                    out.times[f] = result->result;
                }
                else
                    out.values[f] = out.times[f] = 0;
                
                moduleout.times[f] += result->result;
                m_TotalOutput.times[f] += result->result;
                moduleout.weighted_times[f] += result->result * config->weight;                
                m_TotalOutput.weighted_times[f] += result->result * config->weight * pModuleConfig->GetWeight();
            }

            m_NodeOutput.push_back(out);    // kicsit sok a masolas...            
        }

        moduleout.nodecount = (int)m_NodeOutput.size() - moduleout.firstnode;
                      
        // modulok %-os erteke
        for (unsigned f=0; f<m_Files.size(); f++)
        {
            if (moduleout.weighted_times[f])
                moduleout.values[f] = moduleout.weighted_times[reference_file] / moduleout.weighted_times[f];
            else
                moduleout.values[f] = 0;    // FIXME: mi legyen? 0 (= gyorsan lefutott) vagy nagy ertek (=lassan lefutott)?

            //total_sum_result[f] += moduleout.values[f] * pModuleConfig->GetWeight();

            if (moduleout.values[f] > moduleout.maxvalue)
                moduleout.maxvalue = moduleout.values[f];

            //printf("%s::%s weighted: %.4f %.2f%%\n", m_Files[f]->GetDisplayName(), pModuleConfig->GetName(), moduleout.weighted_times[f], moduleout.values[f] * 100.0);
        }

        m_ModuleOutput.push_back(moduleout);
    }

    // total %-os erteke
    for (unsigned f=0; f<m_Files.size(); f++)
    {
        if (m_TotalOutput.weighted_times[reference_file])
            m_TotalOutput.values[f] = m_TotalOutput.weighted_times[reference_file] / m_TotalOutput.weighted_times[f];
        else
            m_TotalOutput.values[f] = 0;    // FIXME: mi legyen? 0 (= gyorsan lefutott) vagy nagy ertek (=lassan lefutott)?


        if (m_TotalOutput.values[f] > m_TotalOutput.maxvalue)
            m_TotalOutput.maxvalue = m_TotalOutput.values[f];

        //printf("%s ideje: %.6f %.2f%%\n", m_Files[f]->GetDisplayName(), m_TotalOutput.weighted_times[f], m_TotalOutput.values[f] * 100.0);
    }

    return true;
}
